unit UDrawGridEx;

interface

uses
  SysUtils, Types, Classes, Graphics, Controls, Messages, Grids, Dialogs, Forms,
  Windows, IBCustomDataSet,
  UTable, UHexeractStruct, ULog, UCellButton;

const
  InvalidCell: TGridCoord = (X: -19920502; Y: -19920205);

type
  TDrawGrid = class(Grids.TDrawGrid)
  private
    FCanDraw: boolean;
    procedure SetCanDraw(const Value: boolean);
  public
    HoveredCell: TGridCoord;
    procedure Up;
    procedure Down;
    function CellRect(const Cell: TGridCoord): TRect; overload;
    function CellBlock: TDataBlock;
    function CellChanged(const NewCell: TGridCoord): boolean;
    function CellInvalid(const Cell: TGridCoord): boolean; inline;
    function IsHeader(const Cell: TGridCoord): boolean; inline;
    procedure UnhoverCell;
    procedure HoverCell(const NewCell: TGridCoord);
    procedure MouseLeave(S: TObject);
    procedure MouseUp(S: TObject; Btn: TMouseButton; Shift: TShiftState;
      X, Y: Integer); reintroduce;
    procedure MouseMove(S: TObject; Shift: TShiftState; X, Y: Integer); reintroduce;
    function BadCell(X, Y: integer): boolean;
    procedure DrawCell(S: TObject; X, Y: Integer; R: TRect;
      State: TGridDrawState); reintroduce;
    procedure DoubleClick(var Msg: TWMMouse); message WM_LBUTTONDBLCLK;
    property CanDraw: boolean read FCanDraw write SetCanDraw;
    constructor Create(AOwner: TComponent); override;
  end;

implementation

function TDrawGrid.BadCell(X, Y: integer): boolean;
begin
  Result := (X or Y = 0) or (X >= Length(DataHeap)) or (Y >= Length(DataHeap[0]));
end;

function TDrawGrid.CellBlock: TDataBlock;
begin
  Result := DataHeap[HoveredCell.X, HoveredCell.Y];
end;

function TDrawGrid.CellChanged(const NewCell: TGridCoord): boolean;
begin
  Result := int64(NewCell) <> int64(HoveredCell);
end;

function TDrawGrid.CellInvalid(const Cell: TGridCoord): boolean;
begin
  Result := int64(Cell) = int64(InvalidCell);
end;

function TDrawGrid.CellRect(const Cell: TGridCoord): TRect;
begin
  Result := CellRect(Cell.X, Cell.Y);
end;

constructor TDrawGrid.Create(AOwner: TComponent);
begin
  inherited;
  HoveredCell := InvalidCell;
  OnMouseMove := MouseMove;
  OnMouseUp := MouseUp;
  SetLength(ShownColumns, TableNumber);
  CallbackRec.Up := Up;
  CallbackRec.Down := Down;
end;

procedure TDrawGrid.DoubleClick(var Msg: TWMMouse);
var
  ACol, ARow: integer;
begin
  MouseToCell(Msg.XPos, Msg.YPos, ACol, ARow);
  if BadCell(ACol, ARow) or DataHeap[ACol, ARow].Empty then
    Exit;
  DataHeap[ACol, ARow].RecountHeight(Canvas.TextHeight, True, ShownColumns);
  RowHeights[ARow] := DataHeap[ACol, ARow].Height + 3;
end;

procedure TDrawGrid.Down;
begin
  CellBlock.DoDown;
  InvalidateCell(HoveredCell.X, HoveredCell.Y);
end;

procedure TDrawGrid.DrawCell(S: TObject; X, Y: Integer; R: TRect;
  State: TGridDrawState);
begin
  if BadCell(X, Y) then
    Exit;
  DataHeap[X, Y].Draw(Canvas, R);
end;

procedure TDrawGrid.HoverCell(const NewCell: TGridCoord);
begin
  HoveredCell := NewCell;
  if CellInvalid(NewCell) then
    Exit;
  CellBlock.DrawingElements := AllDrawingElements;
  Invalidate;
end;

function TDrawGrid.IsHeader(const Cell: TGridCoord): boolean;
begin
  Result := (Cell.X = 0) or (Cell.Y = 0);
end;

procedure TDrawGrid.MouseLeave(S: TObject);
begin
  UnhoverCell;
end;

procedure TDrawGrid.MouseMove(S: TObject; Shift: TShiftState; X, Y: Integer);
var
  Cell: TGridCoord;
  NewHint: string;
begin
  Cell := MouseCoord(X, Y);
  if (Cell.X = -1) and (Cell.Y = -1) then
    Cell := InvalidCell;
  if CellChanged(Cell) then
    UnhoverCell;
  if IsHeader(Cell) then
    Exit;
  if CellInvalid(HoveredCell) then begin
    HoverCell(Cell);
    exit;
  end;

  NewHint := CellBlock.GetHint(Point(X, Y), CellRect(HoveredCell));
  if Hint <> NewHint then
    Application.CancelHint;
  Hint := NewHint;
  Application.ShowHint := True;
end;

procedure TDrawGrid.MouseUp(S: TObject; Btn: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  if CellInvalid(HoveredCell) then
    Exit;
  CellBlock.Click(Point(X, Y), CellRect(HoveredCell));
end;

procedure TDrawGrid.SetCanDraw(const Value: boolean);
begin
  OnDrawCell := nil;
  if Value then
    OnDrawCell := DrawCell;
  FCanDraw := Value;
end;

procedure TDrawGrid.UnhoverCell;
begin
  Application.CancelHint;
  if CellInvalid(HoveredCell) then
    Exit;
  CellBlock.DrawingElements := DefaultDrawingElements;
  Invalidate;
  HoveredCell := InvalidCell;
end;

procedure TDrawGrid.Up;
begin
  CellBlock.DoUp;
  InvalidateCell(HoveredCell.X, HoveredCell.Y);
end;

end.
